home *** CD-ROM | disk | FTP | other *** search
/ Delphi Anthology / aDELPHI.iso / Runimage / Delphi50 / Source / Property Editors / colnedit.pas < prev    next >
Pascal/Delphi Source File  |  1999-08-11  |  22KB  |  814 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Delphi Visual Component Library                 }
  5. {       TCollection Property Editor Dialog              }
  6. {                                                       }
  7. {       Copyright (c) 1999 Borland International        }
  8. {                                                       }
  9. {*******************************************************}
  10.  
  11. unit ColnEdit;
  12.  
  13. interface
  14.  
  15. uses
  16.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  17.   DsgnWnds, StdCtrls, Menus, ExtCtrls, DsgnIntf, ComCtrls, ImgList, ActnList,
  18.   ToolWin, ToolWnds;
  19.  
  20. const
  21.   AM_DeferUpdate = WM_USER + 100;  // avoids break-before-make listview ugliness
  22.  
  23. type
  24.  
  25.   TColOption = (coAdd, coDelete, coMove);
  26.   TColOptions = set of TColOption;
  27.  
  28.   TCollectionEditor = class(TToolbarDesignWindow)
  29.     Panel3: TPanel;
  30.     ListView1: TListView;
  31.     ImageList1: TImageList;
  32.     ToolButton1: TToolButton;
  33.     ToolButton2: TToolButton;
  34.     ToolButton3: TToolButton;
  35.     ToolButton4: TToolButton;
  36.     ToolButton5: TToolButton;
  37.     AddCmd: TAction;
  38.     DeleteCmd: TAction;
  39.     MoveUpCmd: TAction;
  40.     MoveDownCmd: TAction;
  41.     SelectAllCmd: TAction;
  42.     N2: TMenuItem;
  43.     procedure AddClick(Sender: TObject);
  44.     procedure DeleteClick(Sender: TObject);
  45.     procedure ListView1Click(Sender: TObject);
  46.     procedure FormKeyPress(Sender: TObject; var Key: Char);
  47.     procedure FormClose(Sender: TObject; var Action: TCloseAction);
  48.     procedure MoveUpClick(Sender: TObject);
  49.     procedure ListView1DragOver(Sender, Source: TObject; X, Y: Integer;
  50.       State: TDragState; var Accept: Boolean);
  51.     procedure ListView1DragDrop(Sender, Source: TObject; X, Y: Integer);
  52.     procedure MoveDownClick(Sender: TObject);
  53.     procedure FormCreate(Sender: TObject);
  54.     procedure FormDestroy(Sender: TObject);
  55.     procedure FormResize(Sender: TObject);
  56.     procedure ListView1Change(Sender: TObject; Item: TListItem;
  57.       Change: TItemChange);
  58.     procedure SelectAll1Click(Sender: TObject);
  59.     procedure SelectAllCommandUpdate(Sender: TObject);
  60.     procedure SelectionUpdate(Sender: TObject);
  61.     procedure ListView1KeyPress(Sender: TObject; var Key: Char);
  62.   private
  63.     FCollectionPropertyName: string;
  64.     FStateLock: Integer;
  65.     FItemIDList: TList;
  66.     FCollectionClassName: string;
  67.     FSelectionError: Boolean;
  68.     FColOptions: TColOptions;
  69.     function GetRegKey: string;
  70.     procedure SetCollectionPropertyName(const Value: string);
  71.     procedure AMDeferUpdate(var Msg); message AM_DeferUpdate;
  72.     procedure SetColOptions(Value: TColOptions);
  73.   protected
  74.     procedure Activated; override;
  75.     function  CanAdd(Index: Integer): Boolean; virtual;
  76.     procedure LockState;
  77.     procedure UnlockState;
  78.     property StateLock: Integer read FStateLock;
  79.     procedure SelectAll(DoUpdate: Boolean = True);
  80.     procedure SelectNone(DoUpdate: Boolean = True);
  81.   public
  82.     Collection: TCollection;
  83.     Component: TComponent;
  84.     property Options: TColOptions read FColOptions write SetColOptions;
  85.     procedure ComponentDeleted(Component: IPersistent); override;
  86.     procedure FormClosed(AForm: TCustomForm); override;
  87.     procedure FormModified; override;
  88.     function GetItemName(Index, ItemIndex: Integer): string;
  89.     procedure GetSelection;
  90.     procedure SelectionChanged(ASelection: TDesignerSelectionList); override;
  91.     procedure SetSelection;
  92.     procedure UpdateListbox;
  93.     property CollectionPropertyName: string read FCollectionPropertyName
  94.       write SetCollectionPropertyName;
  95.   end;
  96.  
  97.   TCollectionEditorClass = class of TCollectionEditor;
  98.  
  99.   TCollectionProperty = class(TClassProperty)
  100.   public
  101.     procedure Edit; override;
  102.     function GetAttributes: TPropertyAttributes; override;
  103.     function GetEditorClass: TCollectionEditorClass; virtual;
  104.     function GetColOptions: TColOptions; virtual;
  105.   end;
  106.  
  107. procedure ShowCollectionEditor(ADesigner: IDesigner; AComponent: TComponent;
  108.   ACollection: TCollection; const PropertyName: string);
  109. function ShowCollectionEditorClass(ADesigner: IDesigner;
  110.   CollectionEditorClass: TCollectionEditorClass; AComponent: TComponent;
  111.   ACollection: TCollection; const PropertyName: string;
  112.   ColOptions: TColOptions = [coAdd, coDelete, coMove]): TCollectionEditor;
  113.  
  114. implementation
  115.  
  116. {$R *.DFM}
  117.  
  118. uses LibIntf, Registry, TypInfo, DesignConst;
  119.  
  120. type
  121.   TAccessCollection = class(TCollection); // used for protected method access
  122.   TPersistentCracker = class(TPersistent);
  123.  
  124. var
  125.   CollectionEditorsList: TList = nil;
  126.  
  127. function ShowCollectionEditorClass(ADesigner: IDesigner;
  128.   CollectionEditorClass: TCollectionEditorClass; AComponent: TComponent;
  129.   ACollection: TCollection; const PropertyName: string;
  130.   ColOptions: TColOptions): TCollectionEditor;
  131. var
  132.   I: Integer;
  133. begin
  134.   if CollectionEditorsList = nil then
  135.     CollectionEditorsList := TList.Create;
  136.   for I := 0 to CollectionEditorsList.Count-1 do
  137.   begin
  138.     Result := TCollectionEditor(CollectionEditorsList[I]);
  139.     with Result do
  140.       if (Designer = ADesigner) and (Component = AComponent)
  141.         and (Collection = ACollection)
  142.         and (CompareText(CollectionPropertyName, PropertyName) = 0) then
  143.       begin
  144.         Show;
  145.         BringToFront;
  146.         Exit;
  147.       end;
  148.   end;
  149.   Result := CollectionEditorClass.Create(Application);
  150.   with Result do
  151.   try
  152.     Options := ColOptions;
  153.     Designer := ADesigner as IFormDesigner;
  154.     Collection := ACollection;
  155.     FCollectionClassName := ACollection.ClassName;
  156.     Component := AComponent;
  157.     CollectionPropertyName := PropertyName;
  158.     UpdateListbox;
  159.     Show;
  160.   except
  161.     Free;
  162.   end;
  163. end;
  164.  
  165. procedure ShowCollectionEditor(ADesigner: IDesigner; AComponent: TComponent;
  166.   ACollection: TCollection; const PropertyName: string);
  167. begin
  168.   ShowCollectionEditorClass(ADesigner, TCollectionEditor, AComponent,
  169.     ACollection, PropertyName);
  170. end;
  171.  
  172. { TCollectionProperty }
  173.  
  174. procedure TCollectionProperty.Edit;
  175. var
  176.   Obj: TPersistent;
  177. begin
  178.   Obj := GetComponent(0);
  179.   while (Obj <> nil) and not (Obj is TComponent) do
  180.     Obj := TPersistentCracker(Obj).GetOwner;
  181.   ShowCollectionEditorClass(Designer, GetEditorClass,
  182.     TComponent(Obj), TCollection(GetOrdValue), GetName, GetColOptions);
  183. end;
  184.  
  185. function TCollectionProperty.GetAttributes: TPropertyAttributes;
  186. begin
  187.   Result := [paDialog, paReadOnly];
  188. end;
  189.  
  190. function TCollectionProperty.GetEditorClass: TCollectionEditorClass;
  191. begin
  192.   Result := TCollectionEditor;
  193. end;
  194.  
  195. function TCollectionProperty.GetColOptions: TColOptions;
  196. begin
  197.   Result := [coAdd, coDelete, coMove];
  198. end;
  199.  
  200. { TCollectionEditor }
  201.  
  202. procedure TCollectionEditor.Activated;
  203. var
  204.   Msg: TMessage;
  205. begin
  206.   Msg.Msg := WM_ACTIVATE;
  207.   Msg.WParam := 1;
  208.   Designer.IsDesignMsg(Designer.Form, Msg);
  209.   SetSelection;
  210. end;
  211.  
  212. procedure TCollectionEditor.SetColOptions(Value: TColOptions);
  213. begin
  214.   FColOptions := Value;
  215.   AddCmd.Enabled := coAdd in Value;
  216. end;
  217.  
  218. procedure TCollectionEditor.ComponentDeleted(Component: IPersistent);
  219.  
  220.   function IsOwnedBy(Owner, Child: TPersistent): Boolean;
  221.   begin
  222.     Result := False;
  223.     if Owner = nil then Exit;
  224.     while (Child <> nil) and (Child <> Owner) and not (Child is TComponent) do
  225.       Child := TPersistentCracker(Child).GetOwner;
  226.     Result := Child = Owner;
  227.   end;
  228.  
  229. var
  230.   Temp: TPersistent;
  231. begin
  232.   Temp := TryExtractPersistent(Component);
  233.   if Temp = nil then Exit;
  234.   if (Self.Component = nil) or (csDestroying in Self.Component.ComponentState) or
  235.     (Temp = Self.Component) or IsOwnedBy(Temp, Collection) then
  236.   begin
  237.     Collection := nil;  // Component is already in its destructor; collection is gone
  238.     Self.Component := nil;
  239.     Close;
  240.   end
  241.   else if IsOwnedBy(Collection, Temp) then
  242.     PostMessage(Handle, AM_DeferUpdate, 1, 0);
  243. end;
  244.  
  245. procedure TCollectionEditor.FormClosed(AForm: TCustomForm);
  246. begin
  247.   if Designer.Form = AForm then
  248.   begin
  249.     Collection := nil;
  250.     Component := nil;
  251.     Close;
  252.   end;
  253. end;
  254.  
  255. procedure TCollectionEditor.FormModified;
  256. begin
  257.   if Collection <> nil then
  258.   begin
  259.     UpdateListbox;
  260.     if CompLib.GetActiveForm.Designer <> Designer then Exit;
  261.     GetSelection;
  262.   end;
  263. end;
  264.  
  265. function TCollectionEditor.GetItemName(Index, ItemIndex: Integer): string;
  266. begin
  267.   with TAccessCollection(Collection) do
  268.     if GetAttrCount < 1 then
  269.       Result := Format('%d - %s',[ItemIndex, Collection.Items[ItemIndex].DisplayName])
  270.     else Result := GetItemAttr(Index, ItemIndex);
  271. end;
  272.  
  273. function TCollectionEditor.GetRegKey: string;
  274. begin
  275.   Result := DelphiIDE.GetBaseRegKey + '\' + sIniEditorsName + '\Collection Editor';
  276. end;
  277.  
  278. procedure TCollectionEditor.GetSelection;
  279. var
  280.   I: Integer;
  281.   Item: TCollectionItem;
  282.   List: TDesignerSelectionList;
  283. begin
  284.   LockState;
  285.   try
  286.     ListView1.Selected := nil;
  287.   finally
  288.     UnlockState;
  289.   end;
  290.  
  291.   List := TDesignerSelectionList.Create;
  292.   try
  293.     Designer.GetSelections(List);
  294.     if (List.Count = 0) or (List.Count > Collection.Count) then Exit;
  295.     if not ((List[0] = Component) or (List[0] = Collection)
  296.       or (TCollectionEditor(List[0]).GetOwner = Collection)) then Exit;
  297.  
  298.     if List.Count > ListView1.Items.Count then UpdateListbox;
  299.   finally
  300.     List.Free;
  301.   end;
  302.  
  303.   LockState;
  304.   try
  305.     for I := FItemIDList.Count - 1 downto 0 do
  306.     begin
  307.       Item := Collection.FindItemID(Integer(FItemIDList[I]));
  308.       if Item <> nil then
  309.         ListView1.Items[Item.Index].Selected := True
  310.       else FItemIDList.Delete(I);
  311.     end;
  312.   finally
  313.     UnlockState;
  314.   end;
  315. end;
  316.  
  317. procedure TCollectionEditor.LockState;
  318. begin
  319.   Inc(FStateLock);
  320. end;
  321.  
  322. procedure TCollectionEditor.SelectionChanged(ASelection: TDesignerSelectionList);
  323. begin
  324. end;
  325.  
  326. procedure TCollectionEditor.SetCollectionPropertyName(const Value: string);
  327. begin
  328.   if Value <> FCollectionPropertyName then
  329.   begin
  330.     FCollectionPropertyName := Value;
  331.     Caption := Format(sColEditCaption, [Component.Name, DotSep, Value]);
  332.   end;
  333. end;
  334.  
  335. procedure TCollectionEditor.SetSelection;
  336. var
  337.   I: Integer;
  338.   List: TDesignerSelectionList;
  339. begin
  340.   if FSelectionError then Exit;
  341.   try
  342.     if ListView1.SelCount > 0 then
  343.     begin
  344.       List := TDesignerSelectionList.Create;
  345.       try
  346.         FItemIDList.Clear;
  347.         for I := 0 to ListView1.Items.Count - 1 do
  348.           if ListView1.Items[I].Selected then
  349.           begin
  350.             List.Add(Collection.Items[I]);
  351.             FItemIDList.Add(Pointer(Collection.Items[I].ID));
  352.           end;
  353.         Designer.SetSelections(List);
  354.       finally
  355.         List.Free;
  356.       end;
  357.     end
  358.     else
  359.       Designer.SelectComponent(Collection);
  360.   except
  361.     FSelectionError := True;
  362.     Application.HandleException(ExceptObject);
  363.     Close;
  364.   end;
  365. end;
  366.  
  367. procedure TCollectionEditor.UnlockState;
  368. begin
  369.   Dec(FStateLock);
  370. end;
  371.  
  372. procedure TCollectionEditor.UpdateListbox;
  373. var
  374.   I, J: Integer;
  375.  
  376.   procedure UpdateSizes;
  377.   var
  378.     I: Integer;
  379.   begin
  380.     with TRegIniFile.Create(GetRegKey) do
  381.     try
  382.       Width := ReadInteger(FCollectionClassName, 'Width', Width);
  383.       Height := ReadInteger(FCollectionClassName, 'Height', Height);
  384.       Splitter1.Top := Toolbar1.Top + Toolbar1.Height;
  385.       ToolBar1.Visible := ReadBool(FCollectionClassName, 'Toolbar', True);
  386.       Splitter1.Visible := Toolbar1.Visible;
  387.       LargeButtons := ReadBool(FCollectionClassName, 'LargeButtons', False);
  388.       ListView1.HandleNeeded;
  389.       if ListView1.Columns.Count > 1 then
  390.         for I := 0 to ListView1.Columns.Count - 1 do
  391.           ListView1.Column[I].Width := ReadInteger(FCollectionClassName,
  392.             Format('Column%d', [I]), ListView1.Column[I].WidthType);
  393.     finally
  394.       Free;
  395.     end;
  396.   end;
  397.  
  398.   procedure UpdateColumns;
  399.   var
  400.     I: Integer;
  401.   begin
  402.     if (Collection <> nil) and
  403.       (((TAccessCollection(Collection).GetAttrCount > 0) and
  404.       (ListView1.Columns.Count <> TAccessCollection(Collection).GetAttrCount)) or
  405.       ((ListView1.Columns.Count = 0) and
  406.       (TAccessCollection(Collection).GetAttrCount < 1))) then
  407.     begin
  408.       ListView1.HandleNeeded;
  409.       with TAccessCollection(Collection) do
  410.       begin
  411.         if GetAttrCount >= 1 then
  412.           for I := 0 to GetAttrCount - 1 do
  413.             with ListView1.Columns.Add do
  414.             begin
  415.               Caption := GetAttr(I);
  416.               Width := -2;
  417.             end
  418.         else
  419.           with ListView1.Columns.Add do
  420.             Width := -1;
  421.         if GetAttrCount >= 1 then
  422.           ListView1.ShowColumnHeaders := True
  423.         //else
  424.         //  ListView1.Column[0].Width := ListView1.ClientWidth;
  425.       end;
  426.       UpdateSizes;
  427.     end;
  428.   end;
  429.  
  430.   procedure FetchItems(List: TStrings);
  431.   var
  432.     I, J: Integer;
  433.     SubList: TStringList;
  434.   begin
  435.     if Collection <> nil then
  436.       for I := 0 to Collection.Count - 1 do
  437.         if CanAdd(I) then
  438.         begin
  439.           SubList := TStringList.Create;
  440.           for J := 1 to TAccessCollection(Collection).GetAttrCount - 1 do
  441.             SubList.Add(GetItemName(J, I));
  442.           List.AddObject(GetItemName(0, I), SubList);
  443.         end;
  444.  
  445.   end;
  446.  
  447.   function ItemsEqual(ListItems: TListItems; Items: TStrings): Boolean;
  448.   var
  449.     I, J: Integer;
  450.   begin
  451.     Result := False;
  452.     if ListItems.Count <> Items.Count then Exit;
  453.     for I := 0 to ListItems.Count - 1 do
  454.     begin
  455.       if ListItems[I].Caption = Items[I] then
  456.       begin
  457.         for J := 0 to ListItems[I].SubItems.Count - 1 do
  458.           if ListItems[I].SubItems[J] <> TStrings(Items.Objects[I])[J] then
  459.             Exit;
  460.       end
  461.       else
  462.         Exit;
  463.     end;
  464.     Result := True;
  465.   end;
  466.  
  467. var
  468.   TmpItems: TStringList;
  469. begin
  470.   if Collection = nil then Exit;
  471.   LockState;
  472.   try
  473.     TmpItems := TStringList.Create;
  474.     FetchItems(TmpItems);
  475.     try
  476.       if (TmpItems.Count = 0) or not ItemsEqual(ListView1.Items, TmpItems) then
  477.       begin
  478.         ListView1.Items.BeginUpdate;
  479.         try
  480.           UpdateColumns;
  481.           ListView1.Items.Clear;
  482.           for I := 0 to TmpItems.Count - 1 do
  483.             with ListView1.Items.Add do
  484.             begin
  485.               Caption := TmpItems[I];
  486.               for J := 0 to TStrings(TmpItems.Objects[I]).Count - 1 do
  487.                 SubItems.Add(TStrings(TmpItems.Objects[I])[J]);
  488.             end;
  489.         finally
  490.           ListView1.Items.EndUpdate;
  491.         end;
  492.       end;
  493.     finally
  494.       for I := 0 to TmpItems.Count - 1 do
  495.         TStrings(TmpItems.Objects[I]).Free;
  496.       TmpItems.Free;
  497.     end;
  498.   finally
  499.     UnlockState;
  500.   end;
  501. end;
  502.  
  503. procedure TCollectionEditor.AddClick(Sender: TObject);
  504. var
  505.   Item: TListItem;
  506.   PrevCount: Integer;
  507. begin
  508.   SelectNone(False);
  509.   Collection.BeginUpdate;
  510.   try
  511.     PrevCount := Collection.Count + 1;
  512.     Collection.Add;
  513.     { Take into account collections that free items }
  514.     if PrevCount <> Collection.Count then
  515.       UpdateListBox
  516.     else
  517.       ListView1.Selected := ListView1.Items.Add;
  518.   finally
  519.     Collection.EndUpdate;
  520.   end;
  521.   SetSelection;
  522.   Designer.Modified;
  523.   { Focus last added item }
  524.   Item := ListView1.Items[ListView1.Items.Count-1];
  525.   Item.Focused := True;
  526.   Item.MakeVisible(False);
  527. end;
  528.  
  529. procedure TCollectionEditor.DeleteClick(Sender: TObject);
  530. var
  531.   I, J: Integer;
  532. begin
  533.   Collection.BeginUpdate;
  534.   try
  535.     Designer.SetSelections(nil);
  536.     if ListView1.Selected <> nil then
  537.       J := ListView1.Selected.Index
  538.     else J := -1;
  539.     if ListView1.SelCount = Collection.Count then
  540.       Collection.Clear
  541.     else if ListView1.SelCount > 0 then
  542.       for I := ListView1.Items.Count - 1 downto 0 do
  543.         if ListView1.Items[I].Selected then
  544.           Collection.Items[I].Free;
  545.   finally
  546.     Collection.EndUpdate;
  547.   end;
  548.   UpdateListbox;
  549.   if J >= ListView1.Items.Count then
  550.     J := ListView1.Items.Count - 1;
  551.   if (J > -1) and (J < ListView1.Items.Count) then
  552.     ListView1.Selected := ListView1.Items[J];
  553.   SetSelection;
  554.   Designer.Modified;
  555. end;
  556.  
  557. procedure TCollectionEditor.ListView1Click(Sender: TObject);
  558. begin
  559. //  SetSelection;
  560. end;
  561.  
  562. procedure TCollectionEditor.FormKeyPress(Sender: TObject; var Key: Char);
  563. begin
  564.   if Key = #13 then
  565.     DelphiIDE.ModalEdit(#0,Self);
  566. end;
  567.  
  568. procedure TCollectionEditor.FormClose(Sender: TObject;
  569.   var Action: TCloseAction);
  570. var
  571.   I: Integer;
  572. begin
  573.   if Component <> nil then
  574.     Designer.SelectComponent(Component);
  575.   with TRegIniFile.Create(GetRegKey) do
  576.   try
  577.     EraseSection(FCollectionClassName);
  578.     WriteInteger(FCollectionClassName, 'Width', Width);
  579.     WriteInteger(FCollectionClassName, 'Height', Height);
  580.     WriteBool(FCollectionClassName, 'LargeButtons', LargeButtons);
  581.     WriteBool(FCollectionClassName, 'Toolbar', ToolBar1.Visible);
  582.     for I := 0 to ListView1.Columns.Count - 1 do
  583.       WriteInteger(FCollectionClassName, Format('Column%d', [I]),
  584.         ListView1.Column[I].WidthType);
  585.   finally
  586.     Free;
  587.   end;
  588.   Action := caFree;
  589.   LockState;
  590. end;
  591.  
  592.  
  593. procedure TCollectionEditor.MoveUpClick(Sender: TObject);
  594. var
  595.   I, InsPos: Integer;
  596. begin
  597.   if (ListView1.SelCount = 0) or
  598.     (ListView1.SelCount = Collection.Count) then Exit;
  599.  
  600.   InsPos := 0;
  601.   while not ListView1.Items[InsPos].Selected do
  602.     Inc(InsPos);
  603.   if InsPos > 0 then Dec(InsPos);
  604.  
  605.   Collection.BeginUpdate;
  606.   try
  607.      for I := 0 to ListView1.Items.Count - 1 do
  608.        if ListView1.Items[I].Selected then
  609.        begin
  610.          Collection.Items[I].Index := InsPos;
  611.          Inc(InsPos);
  612.        end;
  613.   finally
  614.     Collection.EndUpdate;
  615.   end;
  616.   GetSelection;
  617.   Designer.Modified;
  618. end;
  619.  
  620. procedure TCollectionEditor.MoveDownClick(Sender: TObject);
  621. var
  622.   I, InsPos: Integer;
  623. begin
  624.   if (ListView1.SelCount = 0) or
  625.     (ListView1.SelCount = Collection.Count) then Exit;
  626.  
  627.   InsPos := ListView1.Items.Count - 1;
  628.   while not ListView1.Items[InsPos].Selected do
  629.     Dec(InsPos);
  630.   if InsPos < (ListView1.Items.Count -1) then Inc(InsPos);
  631.  
  632.   Collection.BeginUpdate;
  633.   try
  634.      for I := ListView1.Items.Count - 1 downto 0 do
  635.        if ListView1.Items[I].Selected then
  636.        begin
  637.          Collection.Items[I].Index := InsPos;
  638.          Dec(InsPos);
  639.        end;
  640.   finally
  641.     Collection.EndUpdate;
  642.   end;
  643.   GetSelection;
  644.   Designer.Modified;
  645. end;
  646.  
  647. procedure TCollectionEditor.ListView1DragOver(Sender, Source: TObject; X,
  648.   Y: Integer; State: TDragState; var Accept: Boolean);
  649. var
  650.   Item: TListItem;
  651. begin
  652.   Item := ListView1.GetItemAt(X, Y);
  653.   Accept := (Item <> nil) and (Source = ListView1) and
  654.     (not Item.Selected);
  655. end;
  656.  
  657. procedure TCollectionEditor.ListView1DragDrop(Sender, Source: TObject; X,
  658.   Y: Integer);
  659. var
  660.   Item: TListItem;
  661.   I, J, InsPos: Integer;
  662.   L: TList;
  663. begin
  664.   Item := ListView1.GetItemAt(X, Y);
  665.   if Item <> nil then
  666.     InsPos := Item.Index
  667.   else Exit;
  668.   L := TList.Create;
  669.   try
  670.     for I := 0 to ListView1.Items.Count - 1 do
  671.       if ListView1.Items[I].Selected then
  672.         L.Add(Collection.Items[I]);
  673.  
  674.     Collection.BeginUpdate;
  675.     try
  676.       for I := 0 to L.Count - 1 do
  677.       with TCollectionItem(L[I]) do
  678.       begin
  679.         J := Index;
  680.         Index := InsPos;
  681.         if (J > InsPos) and (InsPos < Collection.Count) then
  682.           Inc(InsPos);
  683.       end;
  684.     finally
  685.       Collection.EndUpdate;
  686.     end;
  687.   finally
  688.     L.Free;
  689.   end;
  690.   GetSelection;
  691.   Designer.Modified;
  692. end;
  693.  
  694.  
  695. procedure TCollectionEditor.FormCreate(Sender: TObject);
  696. begin
  697.   FItemIdList := TList.Create;
  698.   CollectionEditorsList.Add(Self);
  699. end;
  700.  
  701. procedure TCollectionEditor.FormDestroy(Sender: TObject);
  702. begin
  703.   FItemIdList.Free;
  704.   if CollectionEditorsList <> nil then
  705.     CollectionEditorsList.Remove(Self);
  706. end;
  707.  
  708. procedure TCollectionEditor.FormResize(Sender: TObject);
  709. begin
  710.   //if not ListView1.ShowColumnHeaders then
  711.   //  ListView1.Column[0].Width := ListView1.ClientWidth;
  712. end;
  713.  
  714. procedure TCollectionEditor.ListView1Change(Sender: TObject;
  715.   Item: TListItem; Change: TItemChange);
  716. var
  717.   Msg: TMsg;
  718. begin
  719.   if (Change = ctState) and (FStateLock = 0) then
  720.     if not PeekMessage(Msg, Handle, AM_DeferUpdate, AM_DeferUpdate, PM_NOREMOVE) then
  721.       PostMessage(Handle, AM_DeferUpdate, 0, 0);
  722. end;
  723.  
  724. procedure TCollectionEditor.AMDeferUpdate(var Msg);
  725. begin
  726.   if FStateLock = 0 then
  727.   begin
  728.     if TMessage(Msg).WParam = 0 then
  729.       SetSelection
  730.     else
  731.       FormModified;
  732.   end
  733.   else
  734.     PostMessage(Handle, AM_DeferUpdate, TMessage(Msg).WParam, TMessage(Msg).LParam);
  735. end;
  736.  
  737. procedure TCollectionEditor.SelectAll1Click(Sender: TObject);
  738. begin
  739.   SelectAll();
  740. end;
  741.  
  742. procedure TCollectionEditor.SelectionUpdate(Sender: TObject);
  743. var
  744.   Enabled: Boolean;
  745. begin
  746.   Enabled := ListView1.Selected <> nil;
  747.   if Enabled then
  748.     if Sender = DeleteCmd then
  749.       Enabled := coDelete in Options else
  750.     if (Sender = MoveUpCmd) or (Sender = MoveDownCmd) then
  751.       Enabled := coMove in Options;
  752.   (Sender as TAction).Enabled := Enabled;
  753. end;
  754.  
  755. procedure TCollectionEditor.SelectAllCommandUpdate(Sender: TObject);
  756. begin
  757.   (Sender as TAction).Enabled := ListView1.Items.Count > 0;
  758. end;
  759.  
  760. procedure TCollectionEditor.SelectAll(DoUpdate: Boolean);
  761. var
  762.   I: Integer;
  763. begin
  764.   LockState;
  765.   ListView1.Items.BeginUpdate;
  766.   try
  767.     for I := 0 to Listview1.Items.Count-1 do
  768.       Listview1.Items[I].Selected := True;
  769.   finally
  770.     ListView1.Items.EndUpdate;
  771.     UnlockState;
  772.     if DoUpdate then SetSelection;
  773.   end;
  774. end;
  775.  
  776. procedure TCollectionEditor.SelectNone(DoUpdate: Boolean);
  777. var
  778.   I: Integer;
  779. begin
  780.   LockState;
  781.   ListView1.Items.BeginUpdate;
  782.   try
  783.     for I := 0 to Listview1.Items.Count-1 do
  784.       Listview1.Items[I].Selected := False;
  785.   finally
  786.     ListView1.Items.EndUpdate;
  787.     UnlockState;
  788.     if DoUpdate then SetSelection;
  789.   end;
  790. end;
  791.  
  792. procedure TCollectionEditor.ListView1KeyPress(Sender: TObject;
  793.   var Key: Char);
  794. begin
  795.   if Key in ['!'..'~'] then
  796.   begin
  797.     DelphiIDE.ModalEdit(Key, Self);
  798.     Key := #0;
  799.   end;
  800. end;
  801.  
  802. function TCollectionEditor.CanAdd(Index: Integer): Boolean;
  803. begin
  804.   Result := True;
  805. end;
  806.  
  807. initialization
  808.  
  809. finalization
  810.   CollectionEditorsList.Free;
  811.   CollectionEditorsList := nil;
  812. end.
  813.  
  814.